
#include <iostream>
#include <vulkan\vulkan.h>
#include <vector>
#include <type_traits>
#include <utility>

#pragma comment(lib, "vulkan-1.lib")

#include "main.h"

struct devide_property
{
	::VkDevice _dvc = NULL;

	void operator()()
	{
		std::cout << "setup device.\n";
	}

	bool operator==(const devide_property &that) const
	{
		return _dvc == that._dvc;
	}
};

struct renderpass_property
{
	::VkRenderPass _renderpass = NULL;

	void operator()()
	{
		std::cout << "setup renderpass.\n";
	}

	void post_execute()
	{
		std::cout << "post execute renderpass\n";
	}

	bool operator==(const renderpass_property &that) const
	{
		return _renderpass == that._renderpass;
	}
};

struct cmdbufferpool_property
{
	::VkCommandPool _cmdpool = NULL;

	void operator()()
	{
		std::cout << "setup command buffer pool.\n";
	}

	void post_execute()
	{
		std::cout << "post execute cmd buffer pool\n";
	}

	bool operator==(const cmdbufferpool_property &that) const
	{
		return _cmdpool == that._cmdpool;
	}
};

struct cmdbuffer_property
{
	::VkCommandBuffer _cmdbuf = NULL;

	void operator()()
	{
		std::cout << "setup command buffer.\n";
	}

	void post_execute()
	{
		std::cout << "post execute cmdbuffer\n";
	}

	bool operator==(const cmdbuffer_property &that) const
	{
		return _cmdbuf == that._cmdbuf;
	}
};

struct drawable_object
{
	int progid = 0;
	int blendmodeid = 0;

	void operator()()
	{
		std::cout << "draw me.\n";
	}

	template <typename Ty>
	Ty property() const
	{
		return Ty{};
	}
};

using vulkan_exec_tree = elimination_tree
<
	drawable_object,
	devide_property,
	cmdbufferpool_property,
	cmdbuffer_property,
	renderpass_property
>;

int main()
{
	VkApplicationInfo appinfo{};
	appinfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
	appinfo.apiVersion = VK_API_VERSION_1_0;

	VkInstanceCreateInfo instanceCreateInfo{};
	instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
	instanceCreateInfo.pApplicationInfo = &appinfo;

	VkInstance instance;
	auto result = vkCreateInstance(&instanceCreateInfo, NULL, &instance);
	if (result == VK_ERROR_INCOMPATIBLE_DRIVER)
	{
		std::cerr << "Cannot find a compatible vulkan icd.\n";
		return -1;
	}
	else if (result != VK_SUCCESS)
	{
		std::cerr << "Failed to create vk instance.\n";
		std::getchar();
		return -1;
	}

	std::vector<VkPhysicalDevice> phydvcs;
	std::uint32_t cntPhyDvcs;
	result = vkEnumeratePhysicalDevices(instance, &cntPhyDvcs, NULL);
	if (result != VK_SUCCESS)
	{
		std::cerr << "Failed to enumerate physical devices.\n";
		std::getchar();
		return -1;
	}

	phydvcs.resize(cntPhyDvcs);
	(void)vkEnumeratePhysicalDevices(instance, &cntPhyDvcs, phydvcs.data());

	for (auto &dvc : phydvcs)
	{
		std::cout << "Detected one dvc.\n";
	}

	vulkan_exec_tree exectree;
	drawable_object obj;
	exectree.insert(obj);
	exectree.execute();
}